package club.kynb.mall.application.flow.order;

import club.kynb.mall.application.context.OrderSubmitContext;
import club.kynb.mall.application.service.CouponApplicationService;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.NumberUtil;
import cn.hutool.json.JSONUtil;
import club.kynb.mall.application.constant.MallResultCode;
import club.kynb.mall.application.model.model.dto.PreviewOrderDetailDTO;
import club.kynb.mall.application.model.model.dto.PreviewOrderSpuDTO;
import club.kynb.mall.application.model.model.dto.UserReceiveCouponDTO;
import club.kynb.mall.application.model.model.command.SubmitOrderCommand;
import club.kynb.mall.product.constant.CouponSpuRangeEnum;
import club.kynb.mall.product.constant.UserCouponStatusEnum;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.pizza.common.web.exception.Errors;
import org.pizza.util.Checker;
import org.springframework.stereotype.Component;

import java.math.BigDecimal;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
import java.util.stream.Collectors;

/**
 * @author kynb_club@163.com
 * @since 2021/7/7 1:56 下午
 */
@Slf4j
@Component
@AllArgsConstructor
public class CheckUserCouponFilter extends AbstractSubmitOrderFlowFilter {
    private final CouponApplicationService couponApplicationService;

    @Override
    protected void doService(OrderSubmitContext orderSubmitContext) {
        final SubmitOrderCommand command = orderSubmitContext.getCommand();
        if (Objects.nonNull(command.getUserCouponId())) {
            final List<UserReceiveCouponDTO> couponList = couponApplicationService.userCouponList(UserCouponStatusEnum.UN_USE.getStatus());
            final Optional<UserReceiveCouponDTO> first = couponList.stream()
                    .filter(couponDTO -> couponDTO.getId().equals(command.getUserCouponId())).findFirst();
            final UserReceiveCouponDTO userReceiveCouponDTO = first.orElseThrow(() -> Errors.BIZ.exception(MallResultCode.CODE_13005));

            final List<PreviewOrderSpuDTO> spuList = orderSubmitContext.getPreviewOrder().getOrderDetailList().stream()
                    .map(PreviewOrderDetailDTO::getPreviewOrderSpuDTO)
                    .collect(Collectors.toList());
            //检查优惠券是否满足要求
            final boolean canUse = this.orderCanUse(orderSubmitContext,spuList, userReceiveCouponDTO);
            Checker.ifNotThrow(canUse, () -> Errors.BIZ.exception("传入优惠券有误，当前订单不满足使用要求"));

            orderSubmitContext.setUsedCoupon(userReceiveCouponDTO);
        }
    }

    private boolean orderCanUse(OrderSubmitContext orderSubmitContext,List<PreviewOrderSpuDTO> spuList, UserReceiveCouponDTO userReceiveCouponDTO) {
        //全场通用
        if (userReceiveCouponDTO.getSpuRange().equals(CouponSpuRangeEnum.ALL.getType())) {
            final BigDecimal totalAmount = orderSubmitContext.calculateTotalAmount(spuList);
            return NumberUtil.isGreaterOrEqual(totalAmount, new BigDecimal(userReceiveCouponDTO.getThresholdAmount()));
        }
        //指定商品
        final String spuIds = userReceiveCouponDTO.getSpuIds();
        final List<Long> spuIdList = JSONUtil.toList(JSONUtil.parseArray(spuIds), Long.class);
        if (CollectionUtil.isEmpty(spuIdList)) {
            log.error("优惠券配置有误，优惠券ID：{} 类型为指定商品，但未spuId列表为空", userReceiveCouponDTO.getId());
            return false;
        }
        //过滤出优惠券限定的商品
        final List<PreviewOrderSpuDTO> filteredList = spuList
                .stream()
                .filter(productSpuDTO -> spuIdList.contains(Long.valueOf(productSpuDTO.getId())))
                .collect(Collectors.toList());
        final BigDecimal totalAmount = orderSubmitContext.calculateTotalAmount(filteredList);
        return NumberUtil.isGreaterOrEqual(totalAmount, new BigDecimal(userReceiveCouponDTO.getThresholdAmount()));
    }
}
